package com.elastic.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.elastic.model.CreateIndexBean;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @description: es 工具类
 * @author: zyj
 * @create: 2019-12-05 14:19
 **/
@Component
public class Esutil {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RestHighLevelClient restHighLevelClient;

    /**
    * @Description: 创建索引
    * @Param: [indexName: 索引名称, builder: mapping值]
    * @return: boolean: true创建成功
    * @Author: zyj
    * @Date: 2019/12/5
    */
    public boolean createIndex(String indexName, XContentBuilder builder) {
        try {
            if (isIndexExist(indexName)) {
                logger.info("Index is not exits!");
                return true;
            }


            CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
            // 索引配置，number_of_shards索引分片、number_of_replicas副本，默认为1，如果是本地使用单节点，则改为0
            createIndexRequest.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1));
                        createIndexRequest.mapping(builder);

            CreateIndexResponse indexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            // true表示创建成功new CreateIndexRequest(indexName)

            return indexResponse.isAcknowledged();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("create index faild...", e);
        }

        return false;
    }

    /**
    * @Description: 判断
    * @Param: [index]
    * @return: boolean
    * @Author: zyj
    * @Date: 2019/12/5
    */
    public boolean isIndexExist(String index) {
        boolean exists = false;
        try {
            exists = restHighLevelClient.indices().exists(new GetIndexRequest(index), RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("find index false...",e);
        }
        if (exists) {
            logger.info("Index [" + index + "] is exist!");
        } else {
            logger.info("Index [" + index + "] is not exist!");
        }
        return exists;
    }

    /**
    * @Description: 删除索引
    * @Param: [indexName]
    * @return: boolean
    * @Author: zyj
    * @Date: 2019/12/6
    */
    public boolean deleteIndex(String indexName){
        boolean acknowledged = false;
        try {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
            deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
            acknowledged = delete.isAcknowledged();
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("删除索引失败", e);
        }
        return acknowledged;
    }

    /**
    * @Description: 创建索引 默认指定分词器
    * @Param: [createIndexBeans]
    * @return: boolean
    * @Author: zyj
    * @Date: 2019/12/6
    */
    public boolean createIndex(List<CreateIndexBean> createIndexBeans,String indexName) {
        try {
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject()
                    /**这里Disable了“_all”字段，_all字段会把所有的字段用空格连接，然后用“analyzed”的方式index这个字段，这个字段可以被search，但是不能被retrieve。*/
                    //.startObject("_all").field("enabled", false).endObject()
                    .field("properties").startObject();

            /**not_analyzed 不指定分词器*/
            /**拼接数据  builder.field("time").startObject().field("index", "not_analyzed").field("type", "date").field("format", "strict_date_optional_time||epoch_millis").endObject()*/
            for(CreateIndexBean createIndexBean : createIndexBeans){
                String isIndex = createIndexBean.getIsIndex();
                if(null==isIndex||EnumTypes.ES_ISINDEX.getValue().equals(isIndex)){
                    String name = createIndexBean.getName();
                    String type = createIndexBean.getType();
                    String format = createIndexBean.getFormat();
                    String fielddata = createIndexBean.getFielddata();
                    String index = createIndexBean.getIndex();
                    if(null!=name&&!name.isEmpty()&&null!=type&&!type.isEmpty()){
                        builder.field(name).startObject()
                                //.field("term_vector", "with_positions_offsets")
                                .field("type", type);

                        if(null!=index&&index.equals(EnumTypes.ES_INDEX.getValue())){
                            builder.field("index", true)
                                    .field("analyzer", EnumTypes.ES_ANALYZER_IK_MAX.getValue());//hanlp, ik_smart,ik_max_word
                        }

                        if(null!=fielddata&&fielddata.equals(EnumTypes.ES_FILEDDATA.getValue())){
                            builder.field("fielddata", true);
                        }

                        if(null!=format&&!format.isEmpty()){
                            builder.field("format", format);
                        }
                        builder.endObject();
                    }
                }
            }
            builder.endObject().endObject();
            return this.createIndex(indexName, builder);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("创建索引失败", e);
        }

        return false;
    }


    /**
    * @Description: 根据索引插入数据
    * @Param: [data, indexName]
    * @return: boolean
    * @Author: zyj
    * @Date: 2019/12/6
    */
    public boolean inseartData(String articleStr, String indexName){
        try {
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.index(indexName);

            indexRequest.source(articleStr, XContentType.JSON);
            IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);

            if(null!=indexResponse){
                String id = indexResponse.getId();
                String index = indexResponse.getIndex();
                Long version = indexResponse.getVersion();

                logger.info("index:{},id:{}", index, id);
                // 分片处理信息
                ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
                if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                    logger.info("分片处理信息.....",shardInfo.getTotal());
                }
                // 如果有分片副本失败，可以获得失败原因信息
                if (shardInfo.getFailed() > 0) {
                    for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                        String reason = failure.reason();
                        logger.error("副本失败原因：" + reason);
                    }
                }



                if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
                    return true;
                } else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
                    return true;
                }
            }

        } catch (Exception e){
            e.printStackTrace();
            logger.error("插入数据失败",e);
        }


        return false;
    }

    /**
     * @Description: 批量插入数据
     * @Param: [data, indexName]
     * @return: boolean
     * @Author: zyj
     * @Date: 2019/12/6
     */
    public boolean inseartDataBatch(JSONArray articleArray, String indexName){
        try {
            BulkRequest bulkRequest = new BulkRequest();
            for(int i=0; i<articleArray.size(); i++){
                String articleStr = articleArray.getString(i);
                IndexRequest indexRequest = new IndexRequest(indexName);
                indexRequest.source(articleStr, XContentType.JSON);
                bulkRequest.add(indexRequest);
            }
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if(bulkResponse.hasFailures()){
                return false;
            }

            BulkItemResponse [] bulkItemResponses = bulkResponse.getItems();
            for (BulkItemResponse bulkItemResponse: bulkItemResponses){
                String id = bulkItemResponse.getId();
                Long version = bulkItemResponse.getVersion();
                logger.info("插入数据成功!" + indexName + "-" + id + "-" + version);
            }

            return true;
        } catch (Exception e){
            e.printStackTrace();
            logger.error("插入数据失败",e);
        }


        return false;
    }


    /**
    * @Description:根据索引查询数据
    * @Param: [sourceBuilder, indexName]
    * @return: java.lang.String
    * @Author: zyj
    * @Date: 2019/12/6
    */
    public JSONArray queryIndex(SearchSourceBuilder sourceBuilder, String indexName) throws Exception{

        SearchRequest searchRequest = new SearchRequest();

        /** 设置request要搜索的索引和类型*/
        searchRequest.indices(indexName);
        /** 设置SearchSourceBuilder查询属性*/
        searchRequest.source(sourceBuilder);

        /**查询*/
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        logger.info("命中数："+hits.getTotalHits());
        //logger.info("响应字符："+response.toString());
        JSONArray jsonArray = new JSONArray();
        for (SearchHit hit : hits) {

            String sourceAsString = hit.getSourceAsString();
            JSONObject jsonObject = JSON.parseObject(sourceAsString);
            jsonObject.put("id", hit.getId());
            jsonArray.add(jsonObject);
        }

        return jsonArray;
    }

    /**
    * @Description: 根据id删除数据
    * @Param: [id, indexName]
    * @return: boolean
    * @Author: zyj
    * @Date: 2019/12/9
    */
    public boolean deleteData(String id, String indexName){
        DeleteRequest deleteRequest = new DeleteRequest(indexName);
        deleteRequest.id(id);
        try {
            DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
                return false;
            } else {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("删除数据异常",e);
        }

        return false;
    }


    /**
     * @Description: 根据id批量删除数据
     * @Param: [id, indexName]
     * @return: boolean
     * @Author: zyj
     * @Date: 2019/12/9
     */
    public boolean deleteDataBatch(List<String> ids, String indexName){
        try {
            BulkRequest request = new BulkRequest();
            for(int i=0; i<ids.size(); i++){
                request.add(new DeleteRequest(indexName, ids.get(i)));
            }

            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            logger.error("批量删除数据异常", e);
        }

        return false;
    }

    /**
    * @Description: 获取分词结果
    * @Param: [searchContent, ikAnalyzer]
    * @return: java.util.List<java.lang.String>
    * @Author: zyj
    * @Date: 2019/12/31
    */
    public List<String> getIkAnalyzeSearchTerms(String searchContent, String ikAnalyzer) {
        List<String> result = new ArrayList<>();
        try {
            AnalyzeRequest analyzeRequest = new AnalyzeRequest()
                    .text(searchContent)
                    .analyzer(ikAnalyzer);
            List<AnalyzeResponse.AnalyzeToken> tokens = restHighLevelClient.indices().analyze(analyzeRequest, RequestOptions.DEFAULT).getTokens();
            for (AnalyzeResponse.AnalyzeToken token : tokens) {
                result.add(token.getTerm());
            }

            return result;
        } catch (IOException e) {
            logger.error("获取分词结果失败", e);
        }

        return null;
    }

}
